package com.iursp;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import com.iursp.conf.authorize.AuthorizeConfigProvider;
import com.iursp.conf.authorize.SmsCodeAuthenticationSecurityConfig;
import com.iursp.properties.SecurityConstants;
import com.iursp.properties.SecurityProperties;
import com.iursp.validate.code.ValidateCodeSecurityConfig;


@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
	
	@Autowired
    private DataSource dataSource;
	
	@Autowired
	private UserDetailsService userDetailsService;
	
	@Autowired
    private SecurityProperties securityProperties;

	@Autowired
	private AuthorizeConfigProvider authorizeConfigProvider;

    @Autowired 
    private ValidateCodeSecurityConfig validateCodeSecurityConfig;

	@Autowired
	private AuthenticationSuccessHandler validationLoginSuccessHandler;

	@Autowired
	private AuthenticationFailureHandler validationAuthenticationfailureHandler;

	@Autowired
	private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;


    
    /**
     * 
     * @Title: persistentTokenRepository   
     * @Description: 记住我--设置操作表的Repository 
     * @param: @return      
     * @return: PersistentTokenRepository      
     * @throws
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        return tokenRepository;
    }

	/**
	 * 
	 * <p>Title: configure</p>   
	 * <p>authorizeRequests()配置路径拦截，表明路径访问所对应的权限，角色，认证信息。
		  formLogin()对应表单认证相关的配置
		  logout()对应了注销相关的配置
		  httpBasic()可以配置basic登录 </p>   
	 * @param http
	 * @throws Exception   
	 * @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configure(org.springframework.security.config.annotation.web.builders.HttpSecurity)
	 */
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.headers().frameOptions().disable().and()
			.formLogin()// 使用表单登录，不再使用默认httpBasic方式
				.loginPage(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL)// 如果请求的URL需要认证则跳转的URL
				.loginProcessingUrl(SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_FORM)// 处理表单中自定义的登录URL
				.successHandler(validationLoginSuccessHandler)//登录成功处理器，返回JSON
	            .failureHandler(validationAuthenticationfailureHandler)//登录失败处理器
                .and()
                .apply(validateCodeSecurityConfig)//验证码拦截
	            .and()
	            .apply(smsCodeAuthenticationSecurityConfig) //短信验证码拦截
				.and()
			.rememberMe()
				.tokenRepository(persistentTokenRepository())//设置操作表的Repository
				.tokenValiditySeconds(securityProperties.getRememberMeSeconds()) //设置过期时间
				.userDetailsService(userDetailsService)//设置userDetailsService
				.and()
			.logout()
				.logoutUrl("/logout")//默认退出地址/logout
				.logoutSuccessUrl("/login")//退出之后跳转到注册页面
				.and()
            .authorizeRequests()
				.antMatchers(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,
						 SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_FORM,
						 SecurityConstants.DEFAULT_SIGN_IN_MOBILE,
						 SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_MOBILE,
						 "/css/*","/img/**","/thirdlib/**","/js/*","/code/*","/admin/api/**")
				.permitAll()// 以上的请求都不需要认证
				// .antMatchers("/").access("hasRole('USER')")
				.and()
				.csrf().disable();// 关闭csrd拦截

		// 安全模块单独配置
		authorizeConfigProvider.config(http.authorizeRequests());
	}

	  /**
     * 去除角色中role_的前缀
     * 表达式需要.access("hasRole('ADMIN')");
     *
     * @throws Exception
     */
    @Bean
    GrantedAuthorityDefaults grantedAuthorityDefaults() {
        return new GrantedAuthorityDefaults(""); // Remove the ROLE_ prefix
    }
    
	/**
	 * 
	 * <p>Title: configure</p>   
	 * <p>Description: 添加 UserDetailsService， 实现自定义登录校验</p>   
	 * @param auth
	 * @throws Exception   
	 * @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configure(org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder)
	 */
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
	}

	/**
	 * 
	 * @Title: passwordEncoder   
	 * @Description: 密码加密 
	 * @param: @return      
	 * @return: PasswordEncoder      
	 * @throws
	 */
	@Bean
	@ConditionalOnMissingBean(PasswordEncoder.class)
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

}